/**
 * Copyright (c) 2018, 西安星沙网络科技-版权所有
 *
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.waleychain.exchange.service.impl.market;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;

import cn.waleychain.exchange.core.Global;
import cn.waleychain.exchange.core.cache.CacheConts;
import cn.waleychain.exchange.core.constant.DDIC;
import cn.waleychain.exchange.core.entity.PageInfo;
import cn.waleychain.exchange.core.exec.ClientException;
import cn.waleychain.exchange.core.logger.LoggerHelper;
import cn.waleychain.exchange.core.result.RetResultCode;
import cn.waleychain.exchange.core.utils.BigDecimalUtils;
import cn.waleychain.exchange.core.utils.DateUtils;
import cn.waleychain.exchange.core.utils.PageHelper;
import cn.waleychain.exchange.core.vaildate.VaildateHelper;
import cn.waleychain.exchange.dao.CoinInfoMapper;
import cn.waleychain.exchange.dao.DealOrderMapper;
import cn.waleychain.exchange.dao.MarketMapper;
import cn.waleychain.exchange.model.CoinInfo;
import cn.waleychain.exchange.model.Market;
import cn.waleychain.exchange.service.impl.BaseServiceImpl;
import cn.waleychain.exchange.service.market.MarketService;

@Service
public class MarketServiceImpl extends BaseServiceImpl implements MarketService {

	private static final Logger mLog = LoggerFactory.getLogger(MarketServiceImpl.class);
	
	@Autowired
	private MarketMapper marketMapper;
	
	@Autowired
	private CoinInfoMapper coinMapper;
	
	@Autowired
	private DealOrderMapper dealOrderMapper;
	
	@Override
	public PageInfo<Market> fetchMarketPageList(String name, String title, Integer status, String transAreaId, int showCount, int currentPage) throws Exception {

		PageHelper.startPage(currentPage, showCount);
		Page<Market> page = marketMapper.fetchMarketPageList(name, title, status, transAreaId);
		
		return new PageInfo<>(page);
	}

	@Override
	public boolean addMarket(Market market) throws Exception {

		// 币对必填
		if (market == null || market.getBuyCoinId() == null || market.getBuyCoinId() == 0L 
				|| market.getSellCoinId() == null || market.getSellCoinId() == 0L) {
			throw new ClientException(RetResultCode.E11001, "交易币对必填");
		}
		
		// 分别获取买币和卖币的信息
		CoinInfo buyCoin = coinMapper.queryByPrimaryKey(market.getBuyCoinId());
		VaildateHelper.vaildateEntityIsNull(buyCoin);
		CoinInfo sellCoin = coinMapper.queryByPrimaryKey(market.getSellCoinId());
		VaildateHelper.vaildateEntityIsNull(sellCoin);
		
		String name = sellCoin.getName() + Global.MARKET_COIN_NAME_SEPARATOR + buyCoin.getName();
		Market isExist = marketMapper.fetchMarketInfoByName(name);
		VaildateHelper.vaildateBooleanResult(isExist != null, RetResultCode.E11001, "相同交易币种的市场已经存在");
		
		market.setName(name);
		market.setTitle(sellCoin.getTitle().toUpperCase() + Global.MARKET_COIN_TITLE_SEPARATOR + buyCoin.getTitle().toUpperCase());
		market.setIconResId(sellCoin.getIconResId());
		
		boolean bool = marketMapper.insertSelective(market) > 0;
		if (bool) {
			// 刷新市场缓存数据列表
			this.resetCacheMarketList(true);
		}
		
		return bool;
	}

	@Override
	public Market fetchMarketInfoById(Long marketId) throws Exception {

		return marketMapper.fetchMarketInfoById(marketId);
	}

	@Override
	public boolean setMarketStatus(Long marketId, Integer status) throws Exception {

		Market market = new Market();
		market.setMarketId(marketId);
		market.setStatus(status);
		market.setUpdateTime(new Date());
		
		boolean bool = marketMapper.updateByPrimaryKeySelective(market) > 0;
		if (bool) {
			// 刷新市场缓存数据列表
			this.resetCacheMarketList(true);
		}
		
		return bool;
	}

	@Override
	public boolean updateMarketInfo(Market market) throws Exception {

		boolean bool = marketMapper.updateByPrimaryKeySelective(market) > 0;
		if (bool) {
			// 刷新市场缓存数据列表
			this.resetCacheMarketList(true);
		}
		
		return bool;
	}

	@Override
	public void resetCacheMarketList(boolean isForce) throws Exception {
		
		// 获取锁
		boolean isLocked = false;
		try {
			isLocked = this.redisService.lock(CacheConts.CACHE_STATIC_LIST_TRADE_MARKET, CacheConts.LOCK_TRADE_MARKET_KEY, true);
			if (!isLocked) {
				// 未获取到锁
				return;
			}
			
			// 从缓存中获取数据
			List<Market> obj = this.getCacheMarketAllList();
			if (obj == null || isForce) {
				// 数据库中获取
				List<Market> list = marketMapper.fetchMarketAllList();
				String date = DateUtils.format(new Date(), DateUtils.yyyyMMddFormat_);
				Iterator<Market> iter = list.iterator();
				while (iter.hasNext()) {
					Market market = iter.next();
					BigDecimal turnoverPrice = dealOrderMapper.fetchLastClosePrice(market.getMarketId(), date);
					if (turnoverPrice != null) {
						if (market.getZhang() != null && market.getZhang().doubleValue() > 0.0D) {
							market.setBuyLimitMax(BigDecimalUtils.getRoundAmount(turnoverPrice.add(turnoverPrice.multiply(market.getZhang())), Global.RETAIN_DECIMAL_LEN));
						}
						
						if (market.getDie() != null && market.getDie().doubleValue() > 0.0D) {
							market.setSellLimitMin(BigDecimalUtils.getRoundAmount(turnoverPrice.subtract(turnoverPrice.multiply(market.getDie())), Global.RETAIN_DECIMAL_LEN));
						}
					}

					market.setClosePrice(turnoverPrice);
					
					this.redisService.hset(CacheConts.CACHE_STATIC_LIST_TRADE_MARKET, String.valueOf(market.getMarketId()), market);
					market = null;
				}
				
			}
			
		} catch (Exception e) {
			LoggerHelper.printLogErrorNotThrows(mLog, e, "重置刷新交易市场到缓存异常");
		} finally {
			if (isLocked) {
				// 释放锁
				this.redisService.unlock(CacheConts.CACHE_STATIC_LIST_TRADE_MARKET, CacheConts.LOCK_TRADE_MARKET_KEY);
			}
		}
		
	}

	@Override
	public List<Market> fetchCacheMarketAllList() throws Exception {

		List<Market> list = getCacheMarketAllList();
		if (list == null) {
			this.resetCacheMarketList(false);
			list = getCacheMarketAllList();
		}
		
		return list;
	}

	private List<Market> getCacheMarketAllList() {
		Set<String> set = this.redisService.hkey(CacheConts.CACHE_STATIC_LIST_TRADE_MARKET);
		if (set == null || set.isEmpty()) {
			return null;
		}
		
		List<Market> list = new ArrayList<>();
		for (String key : set) {
			String res = this.redisService.hget(CacheConts.CACHE_STATIC_LIST_TRADE_MARKET, key);
			Market market = JSONObject.parseObject(res, Market.class);
			list.add(market);
		}
		
		return list;
	}

	@Override
	public List<Market> fetchCacheMarketVaildList() throws Exception {

		List<Market> rs = this.fetchCacheMarketAllList();
		if (rs != null) {
			Iterator<Market> iter = rs.iterator();
			while (iter.hasNext()) {
				Market market = iter.next();
				if (DDIC.MarketStatus.MARKET_STATUS_1.id != market.getStatus()) {
					iter.remove();
				}
			}
			
			Collections.sort(rs, new Comparator<Market>() {
				public int compare(Market m1, Market m2) {
					return m2.getSorting() - m1.getSorting();
				}
			});
		}

		return rs;
	}

	@Override
	public Market fetchMarketByCache(long marketId) throws Exception {
		String res = this.redisService.hget(CacheConts.CACHE_STATIC_LIST_TRADE_MARKET, String.valueOf(marketId));
		return JSONObject.parseObject(res, Market.class);
	}

	@Override
	public List<Market> fetchMarketListByCoinId(Long coinId) throws Exception {

		List<Market> list = this.fetchCacheMarketVaildList();
		if (list != null && list.size() >= 0) {
			List<Market> finalList = new ArrayList<>();
			for (Market m : list) {
				if (m.getBuyCoinId().compareTo(coinId) == 0 || m.getSellCoinId().compareTo(coinId) == 0) {
					finalList.add(m);
				}
			}
			
			return finalList;
			
		}
		
		return new ArrayList<>(0);
	}
}
